/*
 This program is submitted as part of an assignment in the
 J2E
 Fall Semester 2010-2011 
 School of Computer Science
 Interdisciplinary Center, Hertzelia, Israel 

 http://www.idc.ac.il 

 Exercise No.         : GameController.java
 File Name            : Controller.java
 Names (First Last)   : Itay Sorani, Nadav Ziv, Yonathan Zarsky
 Student No.          : 066530254, 039937883, 039692918
 */

package com.example.logic;
 
import android.app.Activity;
import android.content.Context;

import com.example.models.Building;
import com.example.models.Tower;
import com.example.models.Unit;
import com.example.models.Buildings.CentralBase;
import com.example.models.Buildings.GoblinTavern;
import com.example.models.Buildings.PhoneixNest;
import com.example.models.Buildings.TankWorkshop;
import com.example.models.Buildings.TrollLair;
import com.example.models.Towers.ArrowTower;
import com.example.models.Towers.Catapult;
import com.example.models.Towers.FlameThrower;
import com.example.models.Towers.TeslaCoil;
import com.example.models.Units.DaVinciTank;
import com.example.models.Units.Goblin;
import com.example.models.Units.Phoneix;
import com.example.models.misc.Shot;
import com.example.util.Figure;
import com.example.util.FigureType;
import com.example.util.GameMap;
import com.example.util.GameModel;
import com.example.util.MapCell;
import com.example.util.PathNode;
import com.example.util.Vec2D;
 
/**
 * The main controller. Handles all the logic of the game.
 * In next exercises the controller will be separated into few classes.
 */
public class GameController implements Runnable {
	
	// the time in ms between updates.
	private final static int LOOP_UPDATE_RATE = 40;
	// the maximum number of player in the game. only 2 in current exercise will be used.
	private final static int NUM_OF_PLAYERS = 4;
	// the map width. measured in cells - view will render each cell as 40 pixels.
	private static final int MAP_WIDTH = 38;
	// the map height. measured in cells - view will render each cell as 40 pixels.
	private static final int MAP_HEIGHT = 18;
	// pixels per map cell
	private static final int PIXELS_PER_CELL = 40;
	// maximum number of path nodes allowed in the game. will help with object navigation.
	private static final int MAX_NUMBER_OF_PATH_NODES = 100;
	
	// states for model creation - the player single tap changes current state:
	// no request made
	private static final int MODEL_CREATION_NO_REQUEST_STATE = 0;
	// a request was made - first single tap.
	private static final int MODEL_CREATION_REQUEST_MADE_STATE = 1;
	// chose tower
	private static final int TOWER_CREATION_REQUEST_STATE = 2;
	// chose building
	private static final int BUILDING_CREATION_REQUEST_STATE = 3;
	// specific building was chosen, choose its destiny.
	private static final int BUILDING_CREATION_FINAL_DESTENATION_SELECTION = 4;
	// number of temporary models to help with model creation menu.
	private static final int NUMBER_OF_TEMPORARY_MODELS = 7;
	//number of earned enemy hit points in order to get points bonus.
	private static final int REQUIRED_COLLECTED_ENEMY_HIT_POINTS_FOR_EXTRA_POINTS = 1000;
	//number of bonus points
	private static final int EXTRA_POINTS_BONUS = 20;
	// message for game over with a win
	private static final String PLAYER_WON = "YOU WON!";
	// message for game over with a lose
	private static final String PLAYER_LOST = "YOU LOSE!";
	// message for game over
	private static final String GAME_OVER = "GAME OVER";
	// path node indicator for all players.
	private static final int DIRECT_ALL_PLAYERS_THROUGH_PATHNODE = -1;
	private static final int INTRODUCTION_1 = 1,
							 INTRODUCTION_2 = 2,
							 INTRODUCTION_3 = 3,
							 INTRODUCTION_4 = 4,
							 INTRODUCTION_5 = 5,
							 INTRODUCTION_6 = 6,
							 INTRODUCTION_7 = 7,
							 INTRODUCTION_8 = 8,
							 INTRODUCTION_9 = 9,
							 INTRODUCTION_10 = 10,
							 INTRODUCTION_11 = 11,
							 INTRODUCTION_12 = 12,
							 INTRODUCTION_13 = 13,
							 INTRODUCTION_14 = 14,
							 ENEMY_ADDS_TOWER1 = 26,
							 ENEMY_ADDS_TOWER2 = 29,
							 ENEMY_ADDS_BUILDING2 = 32;
	private static final int timeBetweenSteps = 3000;
	private static final String IN_GAME_MESSAGE = "IN GAME MESSAGE ";
	private static final String NOT_ENOUGH_POINTS = "NOT ENOUGH POINTS !!";
	private static final String MESSAGE_NUMBER_1 = "Welcome to BaseWars game";
	private static final String MESSAGE_NUMBER_2 = "All Hell Broke loose...";
	private static final String MESSAGE_NUMBER_3 = "War is upon us and you have been chosen to protect your home land.";
	private static final String MESSAGE_NUMBER_4 = "This is your enemy main base";
	private static final String MESSAGE_NUMBER_5 = "Your enemy has been gathering forces to attack us";
	private static final String MESSAGE_NUMBER_6 = "Buildings will keep on sending forces towards us and new building would be created over time";
	private static final String MESSAGE_NUMBER_7 = "Enemy towers are placed to protect his main base from our troops"; 
	private static final String MESSAGE_NUMBER_8 = "This is your main base";
	private static final String MESSAGE_NUMBER_9 = "Build your first tower to defend off enemy forces";
	private static final String MESSAGE_NUMBER_10 = "Use points gathered from killing enemy forces in order to build towers or offensive buildings";
	private static final String MESSAGE_NUMBER_11 = "Offensive buildings will be used to send forces into battle";
	private static final String MESSAGE_NUMBER_12 = "Choose an arrow to direct them towards your enemy main base, otherwise they would be lost";
	private static final String MESSAGE_NUMBER_13 = "Buildings and Towers can be only placed on mulch, choose their position carefully.";
	private static final String MESSAGE_NUMBER_14 = "GOOD LUCK! our fate is in your hands...";
	private static final String MESSAGE_NUMBER_15 = "The enemy is building towers for protection...";
	private static final String MESSAGE_NUMBER_16 = "The enemy is building buildings to attack...";
	
	private static GameController instance;
	// temporary models in order to help with model creation menu.
	private Figure[] TemporaryModels;
	// reference to the game context
	private Context context;
	// players information
	private Player[] players;

	// an array holding game figures
	private GameModel models;
	// the game map, holds all relevant information regarding figures position. 
	private GameMap map;
	// an array holding the game path nodes.
	private PathNode[] pathNodes;
	// number of path nodes.
	private int pathNodesCounter;
	// pixels per cell
	private int pixelsPerCell;
	// the current model creation state.
	private int modelCreationState;
	private int currentScheduleState;
	private int timeTillNextScheduleState;
	
	private String messageToDisplay = "";
	private boolean isCanvasMoving;
	private float xCanvasMoveDestination;
    private float yCanvasMoveDestination;
    private boolean isEnteringScheduler;
    private boolean isStateEven;
	/**
	 * Returns the singleton instance of the controller.
	 */
	public static synchronized GameController getInstance() {
		if (instance == null) {
			instance = new GameController();
		}
		return instance;
	}

	/*
	 *  Controller constructor.
	 *  Receives the context as an input
	 */
	public GameController() {
		this.players = new Player[NUM_OF_PLAYERS];
		for (int i = 0; i < NUM_OF_PLAYERS; i++) {
			players[i] = new Player();
		}
		this.models = new GameModel();
		this.map = new GameMap(MAP_WIDTH, MAP_HEIGHT);
		this.pathNodes = new PathNode[MAX_NUMBER_OF_PATH_NODES];
		this.pathNodesCounter = 0;
		this.pixelsPerCell = PIXELS_PER_CELL;
		this.modelCreationState = MODEL_CREATION_NO_REQUEST_STATE;
		this.TemporaryModels = new Figure[NUMBER_OF_TEMPORARY_MODELS];
		this.currentScheduleState = 26;//INTRODUCTION_1;
		timeTillNextScheduleState = timeBetweenSteps;
		isEnteringScheduler = true;
		isStateEven = false;
		initMap();
	}

	// the game main loop. update every LOOP_UPDATE_RATE.
	public void gameLoop() {
		while (true) {
			update();
			try {
				Thread.sleep(LOOP_UPDATE_RATE);
			} catch (InterruptedException e) {

			}

		}
	}

	// update all the game main figures according to their type.
	public void update() {
		runSchedule();
		Figure model;
		for (int i = 0; i < models.getNumOfModels(); i++) {
			model = models.getModel(i);
			//if its a building
			if (model != null) {
				if (model instanceof Building) {
					Building building = (Building) model;
					// update production rate, and check if needs to create a new unit.
					building.updateProductionRate();
					if (building.getTimeTillNextProduction() == 0) {
						createNewUnit(building);
					}
				}
				//if its a unit
				if (model instanceof Unit) {
					Unit unit = (Unit)model;
					moveUnit(unit);
				}
				//if its a tower
				if (model instanceof Tower) {
					Tower tower = (Tower) model;
					tower.updateShootingRate();
				}
				if (model instanceof Shot) {
					Shot shot = (Shot)model;
					shot.update();
					Unit unit = shot.getShotDestination();
					if ((shot.getPosition().getDist(unit.getPosition()) <= 0.5) && (unit.getHitPoints() <= 0)) {
						models.removeModel(unit);
						Player player = players[shot.getShootingTower().getPlayerId()];
						int collectedenemyHitpoint = player.getCollectedenemyHitpoint();
						player.setCollectedenemyHitpoint(collectedenemyHitpoint - unit.getInitialHitPoints());
						if (player.getCollectedenemyHitpoint() > REQUIRED_COLLECTED_ENEMY_HIT_POINTS_FOR_EXTRA_POINTS) {
							player.setPoints(player.getPoints() + EXTRA_POINTS_BONUS);
							player.setCollectedenemyHitpoint(0);
						}
					}
				}
			}
		}
	}
	
	//sets the GameModel
	public void setModels (GameModel models) {
		this.models = models;
	}
	
	//sets the GameModel
	public GameModel getModels () {
		return (this.models);
	}

	@Override
	public void run() {
		gameLoop();
	}
	
	// Initializing the game map. when each level should have a different init. 
	// in the next exercise the each level would be init from a given text file, 
	// and this function would parse the given text file. 
	public void initMap(){
		//create a new central base
		CentralBase centralBase = new CentralBase(context);
		players[0].setCentralBase(centralBase);
		centralBase.setPosition(0,8.5f);
        addNewBuilding(0, centralBase);
		PathNode pathNode = new PathNode(0,8.5f, DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
        
		//create a new central base
        centralBase = new CentralBase(context);
        players[1].setCentralBase(centralBase);
        centralBase.setPosition(19.5f,0);
        addNewBuilding(1, centralBase);
        pathNode = new PathNode(19.5f,0,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		
		//create a new central base
		centralBase = new CentralBase(context);
        players[2].setCentralBase(centralBase);
        centralBase.setPosition(37,8.5f);
        addNewBuilding(2, centralBase);
        pathNode = new PathNode(37,8.5f,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
        
		//create a new central base
		centralBase = new CentralBase(context);
        players[3].setCentralBase(centralBase);
        centralBase.setPosition(19.5f,17);
        addNewBuilding(3, centralBase);
        pathNode = new PathNode(19.5f,17,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		
		//create path nodes
		pathNode = new PathNode(19,8,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		pathNode = new PathNode(19,9,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		pathNode = new PathNode(20,8,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		pathNode = new PathNode(20,9,DIRECT_ALL_PLAYERS_THROUGH_PATHNODE);
		addPathNode(pathNode);
		
		for (int i = 1; i < MAP_WIDTH / 3; i++) {
			pathNode = new PathNode(i * 3,8,0);
			addPathNode(pathNode);
			pathNode = new PathNode(i * 3,9,2);
			addPathNode(pathNode);
		}
		
		for (int i = 1; i < MAP_HEIGHT / 3; i++) {
			pathNode = new PathNode(19,i*3,0);
			addPathNode(pathNode);
			pathNode = new PathNode(20,i*3,2);
			addPathNode(pathNode);
		}
		// Creates attack building for the enemy 
        TankWorkshop tankWorkshop = new TankWorkshop(context);
        tankWorkshop.setPosition(33,10);
        tankWorkshop.setFinalDestination(players[0].getCentralBase());
        addNewBuilding(2, tankWorkshop);
        tankWorkshop.setProductionRate(20 * 40);
	}
	
	// given a building this method creates a new unit figure. 
	//it determines which unit should be created, its position and its player id. 
	private void createNewUnit (Building building) {
		Unit unit;
		if (building instanceof GoblinTavern) {
			unit = new Goblin(context);
			if (building.getPlayerId() == 0) {
				unit.setPosition(building.getX() + 1,building.getY()+ 0.5f);
			} else if (building.getPlayerId() == 2) {
				unit.setPosition(building.getX() - 1,building.getY()+ 0.5f);
			}
			unit.setFinalDestenation(building.getFinalDestination().getPosition());
			unit.setPlayerId(building.getPlayerId());
			models.addFigure(unit);
			setNewDirection(unit);
		
		} else if (building instanceof PhoneixNest) {
			unit = new Phoneix(context);
			if (building.getPlayerId() == 0) {
				unit.setPosition(building.getX() + 1,building.getY()+ 0.5f);
			} else if (building.getPlayerId() == 2) {
				unit.setPosition(building.getX() - 1,building.getY()+ 0.5f);
			}
			unit.setFinalDestenation(building.getFinalDestination().getPosition());
			unit.setPlayerId(building.getPlayerId());
			models.addFigure(unit);
			setNewDirection(unit);
			
		
		} else if (building instanceof TankWorkshop) {
			unit = new DaVinciTank(context);
			if (building.getPlayerId() == 0) {
				unit.setPosition(building.getX() + 1,building.getY()+ 0.5f);
			} else if (building.getPlayerId() == 2) {
				unit.setPosition(building.getX() - 1,building.getY()+ 0.5f);
			}
			unit.setFinalDestenation(building.getFinalDestination().getPosition());
			unit.setPlayerId(building.getPlayerId());
			models.addFigure(unit);
			setNewDirection(unit);
		
		} else if (building instanceof GoblinTavern) {
			unit = new Goblin(context);
			if (building.getPlayerId() == 0) {
				unit.setPosition(building.getX() + 1,building.getY()+ 0.5f);
			} else if (building.getPlayerId() == 2) {
				unit.setPosition(building.getX() - 1,building.getY()+ 0.5f);
			}
			unit.setFinalDestenation(building.getFinalDestination().getPosition());
			unit.setPlayerId(building.getPlayerId());
			models.addFigure(unit);
			setNewDirection(unit);
		} 
	}
	
	// this function adds a new building figure to the game and updates the points of the owner.
	private void addNewBuilding (int playerId, Building building) {
		if (players[playerId].getPoints() < building.getCost() && playerId != 2) {
			messageToDisplay = IN_GAME_MESSAGE + NOT_ENOUGH_POINTS;
		} else {
			models.addFigure(building);
			map.addModel((int)building.getX(), (int)building.getY(), building);
			building.setPlayerId(playerId);
			players[playerId].setPoints(players[playerId].getPoints() - building.getCost());
			setBuildingOrTowerDirection(building, playerId);
		}
	}
	
	// adds a new tower figure to the game and updates the points of the owner.
	private void addNewTower (int playerId, Tower tower) {
		if (players[playerId].getPoints() < tower.getCost() && playerId != 2) {
			messageToDisplay = IN_GAME_MESSAGE + NOT_ENOUGH_POINTS;
		} else {
			models.addFigure(tower);
			map.addModel((int)tower.getX(), (int)tower.getY(), tower);
			tower.setPlayerId(playerId);
			setBuildingOrTowerDirection(tower, playerId);
			int x = (int)tower.getX();
			int y = (int)tower.getY();
			int range = tower.getRange();
			for (int i = (x - range); i <= (x + range); i++) {
				for (int j = (y - range); j <= (y + range); j++) {
					if (i >=0 && i < MAP_WIDTH && j >= 0 && j < MAP_HEIGHT) {
						map.addTowerInRange(i, j, tower);
					}
				}
			}
			players[playerId].setPoints(players[playerId].getPoints() - tower.getCost());
		}
	}
	
	// Sets the direction of the tower/building so it will face the right way
	private void setBuildingOrTowerDirection (Figure figure, int playerId) {
		switch (playerId) {
			case 0:
				figure.setDirection(new Vec2D(1.f,0.f));
				break;
			case 1:
				figure.setDirection(new Vec2D(0.f,1.f));
				break;
			case 2:
				figure.setDirection(new Vec2D(-1.f,0.f));
				break;
			case 3:
				figure.setDirection(new Vec2D(0.f,-1.f));
				break;
		}
	}
	
	// removes a given tower from the game. for next exercises where towers could be sold.
	private void removeTower (int playerId, Tower tower) {
		models.removeModel(tower);
		map.removeModel(tower);
		int x = (int)tower.getX();
		int y = (int)tower.getY();
		int range = tower.getRange();
		//add range nodes in the map on appropriate cells according to the tower range
		for (int i = (x - range); i <= (x + range); i++) {
			for (int j = (y - range); j <= (y + range); j++) {
				Tower[] towersInrange = map.getCell(i, j).getTowersInRange(); 
				for (int k = 0; k < map.getCell(i, j).getNumberOfRangeNodes(); k++) {
					if (towersInrange[k].getId() == tower.getId()) {
					map.removeTowerInRange(towersInrange[k]);
					}
				}
			}
		}
		models.removeModel(tower);
	}
	
	// adds a new path node to the map to help with figures navigation.
	public void addPathNode (PathNode pathNode) {
		this.pathNodes[pathNodesCounter] = pathNode;
		pathNode.setPathNodeId(pathNodesCounter);
		pathNodesCounter++;
		map.setPathNode(pathNode);
	}
	
	// removes a given path node from the map.
	public void removePathNode (PathNode pathNode) {
		int temp = pathNode.getPathNodeId();
		this.pathNodes[temp] = this.pathNodes[this.pathNodesCounter - 1];
		this.pathNodesCounter--;
		this.pathNodes[temp].setPathNodeId(temp);
	}
	
	// when entering a path node, this method should determine the new direction for a given unit.
	private void setNewDirection (Unit unit) {
		PathNode nextPathNode = null;
		double minLength = Integer.MAX_VALUE;
		for (int i = 0; i < pathNodesCounter; i++) {
			//if the path nodes is the same as the unit player id
			if ((unit.getPlayerId() == pathNodes[i].getPathNodeDirectedPlayer()) ||
				(pathNodes[i].getPathNodeDirectedPlayer() == DIRECT_ALL_PLAYERS_THROUGH_PATHNODE)) {
				//select best next path node for the given unit
				if (unit.getPosition().getDist(unit.getFinalDestenation()) - 1 >  pathNodes[i].getPosition().getDist(unit.getFinalDestenation())) {
					if (unit.getPosition().getDist(pathNodes[i].getPosition()) < minLength){
						minLength = unit.getPosition().getDist(pathNodes[i].getPosition());
						nextPathNode = pathNodes[i];
					}	
				}
				
			}
		}
		// if next path node is not null, set the unit direction
		if (nextPathNode != null) {
			Vec2D vec = new Vec2D(nextPathNode.getX() - unit.getX(),nextPathNode.getY() - unit.getY() );
			vec.normalize();
			unit.setDirection(vec);

		} else {
			unit.setDirection(new Vec2D(0,0));
		}
	}
	
	// while moving units this method handles all types of unit intersections (with path nodes, central base, towers range, etc).
	private void moveUnit(Unit unit) {
		unit.getX();
		unit.getDirection();
		unit.getVelocity();
		unit.setPosition(unit.getX() + unit.getDirection().getX() * unit.getVelocity(), unit.getY() + unit.getDirection().getY() * unit.getVelocity());
		//if unit is still in the game map boundaries.
		if (((int)unit.getX() >= 0 && (int)unit.getY() >= 0) &&
				((int)unit.getX() < MAP_WIDTH && (int)unit.getY() < MAP_HEIGHT)) {	
			// check possible cell intersection 
			MapCell cell[] = new MapCell[4];
			cell[0] = map.getCell((int)(unit.getX()),((int)(unit.getY())));
			cell[1] = map.getCell((int)(unit.getX() + ((float)unit.getWidth()) / pixelsPerCell),((int)(unit.getY())));
			cell[2] = map.getCell((int)(unit.getX()),((int)(unit.getY() + ((float)unit.getHeight()) / pixelsPerCell)));
			cell[3] = map.getCell((int)(unit.getX() + ((float)unit.getWidth()) / pixelsPerCell),((int)(unit.getY() + ((float)unit.getHeight()) / pixelsPerCell)));
			
			for (int i = 0; i < 4; i++) {
				
				//if its a central base, dispose unit and decrease central base hit points.
				if ((cell[i].getModel() != null) && ((cell[i].getModel() instanceof CentralBase))){
					models.removeModel(unit);
					CentralBase centralBase =  ((CentralBase)(cell[i].getModel()));
					centralBase.setHitPoints(centralBase.getHitPoints() - 1);
					// if central base has no more hit points, game over.
					if (centralBase.getHitPoints() == 0) {
						if (centralBase.getPlayerId() == 0) {
							messageToDisplay = GAME_OVER +", " + PLAYER_LOST;
						} else{ // if (centralBase.getPlayerId() == 2) {
							messageToDisplay = GAME_OVER + ", " + PLAYER_WON;
						}
					}
					break;
				}
				
				// if its a path node and the path node meets with the unit player id, set new direction
				PathNode pathNode = cell[i].getPathNode();
				if ((pathNode != null) &&
					(unit.getPlayerId() == pathNode.getPathNodeDirectedPlayer() || pathNode.getPathNodeDirectedPlayer() == DIRECT_ALL_PLAYERS_THROUGH_PATHNODE)) {
					setNewDirection (unit);
				}
				
				// if this cell is a range cell, get the tower and check if tower can shoot.
				// if so handle the unit hit points.
				if (cell[i].getNumberOfRangeNodes() > 0) {
					for (int j = 0; j < cell[i].getNumberOfRangeNodes(); j++) { 
						Tower tower = cell[i].getTowersInRange()[j];
						if (tower.getPlayerId() != unit.getPlayerId() && tower.CanShoot()) {
							tower.setCanShoot(false);
							Shot shot = new Shot(tower,unit);
							models.addFigure(shot);
							unit.setHitPoints(unit.getHitPoints() - tower.getDamage(unit));
							break;
						}
					}
				}
			}
		}
	}
	
	//return pixels per cell
	public int getPixelsPerCell() {
		return pixelsPerCell;
	}
	
	// set the user request listener.
	public void onMoveRequest(float dx, float dy) {

	}

	public void onNewModelCreationRequest (int xTouchPosition, int yTouchPosition) {
		switch (modelCreationState) {

		// first attempt to add a building or a tower
		case MODEL_CREATION_NO_REQUEST_STATE :

			MapCell mapCell = map.getCell(xTouchPosition,yTouchPosition);
			if ((mapCell == null) || (mapCell.getModel() == null)) {
				modelCreationState = MODEL_CREATION_REQUEST_MADE_STATE;
				TemporaryModels[0] = new ArrowTower(context);
				TemporaryModels[0].setPosition(xTouchPosition - 1, yTouchPosition);
				setBuildingOrTowerDirection(TemporaryModels[0], 0);
				models.addFigure(TemporaryModels[0]);
				TemporaryModels[1] = new Building(context);
				TemporaryModels[1].setPosition(xTouchPosition + 1, yTouchPosition);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[6] = new Figure(context);
				TemporaryModels[6].setPosition(xTouchPosition, yTouchPosition);
			}
			break;

		case MODEL_CREATION_REQUEST_MADE_STATE :

			//if have chosen tower
			if (TemporaryModels[0].getX() == xTouchPosition && TemporaryModels[0].getY() == yTouchPosition) {

				modelCreationState = TOWER_CREATION_REQUEST_STATE;
				models.removeModel(TemporaryModels[0]);
				models.removeModel(TemporaryModels[1]);
				TemporaryModels[0] = new ArrowTower(context);
				TemporaryModels[0].setPosition(xTouchPosition - 1, yTouchPosition - 1);
				setBuildingOrTowerDirection(TemporaryModels[0], 0);
				models.addFigure(TemporaryModels[0]);
				TemporaryModels[1] = new Catapult(context);
				TemporaryModels[1].setPosition(xTouchPosition + 1, yTouchPosition - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[2] = new FlameThrower(context);
				TemporaryModels[2].setPosition(xTouchPosition - 1, yTouchPosition + 1);
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				TemporaryModels[3] = new TeslaCoil(context);
				TemporaryModels[3].setPosition(xTouchPosition + 1, yTouchPosition + 1);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);

				// if have chosen building
			} else if (TemporaryModels[1].getX() == xTouchPosition && TemporaryModels[1].getY() == yTouchPosition) {
				modelCreationState = BUILDING_CREATION_REQUEST_STATE;
				models.removeModel(TemporaryModels[0]);
				models.removeModel(TemporaryModels[1]);
				TemporaryModels[0] = new GoblinTavern(context);
				TemporaryModels[0].setPosition(xTouchPosition - 1, yTouchPosition - 1);
				((Building)(TemporaryModels[0])).setProductionRate(Integer.MAX_VALUE);
				setBuildingOrTowerDirection(TemporaryModels[0], 0);
				models.addFigure(TemporaryModels[0]);
				TemporaryModels[1] = new PhoneixNest(context);
				TemporaryModels[1].setPosition(xTouchPosition + 1, yTouchPosition - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				((Building)(TemporaryModels[1])).setProductionRate(Integer.MAX_VALUE);
				TemporaryModels[2] = new TankWorkshop(context);
				TemporaryModels[2].setPosition(xTouchPosition - 1, yTouchPosition + 1);
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				((Building)(TemporaryModels[2])).setProductionRate(Integer.MAX_VALUE);
				TemporaryModels[3] = new TrollLair(context);
				TemporaryModels[3].setPosition(xTouchPosition + 1, yTouchPosition + 1);
				((Building)(TemporaryModels[3])).setProductionRate(Integer.MAX_VALUE);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);
				// no selection has been made
			} else {
				models.removeModel(TemporaryModels[0]);
				models.removeModel(TemporaryModels[1]);
				modelCreationState = MODEL_CREATION_NO_REQUEST_STATE;
			}
			break;

			// determine which tower has been chosen
		case TOWER_CREATION_REQUEST_STATE:
			modelCreationState = MODEL_CREATION_NO_REQUEST_STATE;
			for (int i = 0; i < 4; i++) {
				models.removeModel(TemporaryModels[i]);
			}
			if (TemporaryModels[0].getX() == xTouchPosition && TemporaryModels[0].getY() == yTouchPosition) {
				ArrowTower arrowTower = new ArrowTower(context);
				arrowTower.setPosition(xTouchPosition + 2, yTouchPosition + 1);
				addNewTower(0, arrowTower);
			} else if (TemporaryModels[1].getX() == xTouchPosition && TemporaryModels[1].getY() == yTouchPosition) {
				Catapult catapult = new Catapult(context);
				catapult.setPosition(xTouchPosition, yTouchPosition + 1);
				addNewTower(0, catapult);
			} else if (TemporaryModels[2].getX() == xTouchPosition && TemporaryModels[2].getY() == yTouchPosition) {
				FlameThrower flameThrower = new FlameThrower(context);
				flameThrower.setPosition(xTouchPosition + 2, yTouchPosition - 1);
				addNewTower(0, flameThrower);
			} else if (TemporaryModels[3].getX() == xTouchPosition && TemporaryModels[3].getY() == yTouchPosition) {
				TeslaCoil teslaCoil = new TeslaCoil(context);
				teslaCoil.setPosition(xTouchPosition, yTouchPosition - 1);
				addNewTower(0, teslaCoil);
			}
			break;
			// determine which building has been chosen
		case BUILDING_CREATION_REQUEST_STATE:
			modelCreationState = BUILDING_CREATION_FINAL_DESTENATION_SELECTION;
			int selectedBuilding = -1;
			for (int i = 0; i < 4; i++) {
				if (!(TemporaryModels[i].getX() == xTouchPosition && TemporaryModels[i].getY() == yTouchPosition)) {
					models.removeModel(TemporaryModels[i]);
				} else {
					selectedBuilding = i;
				}
			}

			if (selectedBuilding == -1) {
				modelCreationState = MODEL_CREATION_NO_REQUEST_STATE;
			} else {
				TemporaryModels[5] = TemporaryModels[selectedBuilding];
			}

			switch (selectedBuilding) {
			// Incase building a goblinTavern is requested
			case 0:
				GoblinTavern goblinTavern = new GoblinTavern(context);
				goblinTavern.setPosition(TemporaryModels[6].getX(), TemporaryModels[6].getY());
				TemporaryModels[0] = goblinTavern;
				TemporaryModels[1] = new Figure(context);
				TemporaryModels[1].setName("ArrowLeft");
				TemporaryModels[1].setFigureType(FigureType.general);
				TemporaryModels[1].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[2] = new Figure(context);
				TemporaryModels[2].setName("ArrowStright");
				TemporaryModels[2].setFigureType(FigureType.general);
				TemporaryModels[2].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY());
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				TemporaryModels[3] = new Figure(context);
				TemporaryModels[3].setName("ArrowRight");
				TemporaryModels[3].setFigureType(FigureType.general);
				TemporaryModels[3].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() + 1);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);
				break;
				// Incase building a phoneixNest is requested
			case 1:
				PhoneixNest phoneixNest = new PhoneixNest(context);
				phoneixNest.setPosition(TemporaryModels[6].getX(), TemporaryModels[6].getY());
				TemporaryModels[0] = phoneixNest;
				TemporaryModels[1] = new Figure(context);
				TemporaryModels[1].setName("ArrowLeft");
				TemporaryModels[1].setFigureType(FigureType.general);
				TemporaryModels[1].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[2] = new Figure(context);
				TemporaryModels[2].setName("ArrowStright");
				TemporaryModels[2].setFigureType(FigureType.general);
				TemporaryModels[2].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY());
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				TemporaryModels[3] = new Figure(context);
				TemporaryModels[3].setName("ArrowRight");
				TemporaryModels[3].setFigureType(FigureType.general);
				TemporaryModels[3].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() + 1);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);
				break;
				// Incase building a tankWorkShop is requested	
			case 2:
				TankWorkshop tankWorkshop = new TankWorkshop(context);
				tankWorkshop.setPosition(TemporaryModels[6].getX(), TemporaryModels[6].getY());
				TemporaryModels[0] = tankWorkshop;
				TemporaryModels[1] = new Figure(context);
				TemporaryModels[1].setName("ArrowLeft");
				TemporaryModels[1].setFigureType(FigureType.general);
				TemporaryModels[1].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[2] = new Figure(context);
				TemporaryModels[2].setName("ArrowStright");
				TemporaryModels[2].setFigureType(FigureType.general);
				TemporaryModels[2].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY());
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				TemporaryModels[3] = new Figure(context);
				TemporaryModels[3].setName("ArrowRight");
				TemporaryModels[3].setFigureType(FigureType.general);
				TemporaryModels[3].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() + 1);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);
				break;
				// Incase building a TrollLair is requested
			case 3:
				TrollLair trollLair = new TrollLair(context);
				trollLair.setPosition(TemporaryModels[6].getX(), TemporaryModels[6].getY());
				TemporaryModels[0] = trollLair;
				TemporaryModels[1] = new Figure(context);
				TemporaryModels[1].setName("ArrowLeft");
				TemporaryModels[1].setFigureType(FigureType.general);
				TemporaryModels[1].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() - 1);
				setBuildingOrTowerDirection(TemporaryModels[1], 0);
				models.addFigure(TemporaryModels[1]);
				TemporaryModels[2] = new Figure(context);
				TemporaryModels[2].setName("ArrowStright");
				TemporaryModels[2].setFigureType(FigureType.general);
				TemporaryModels[2].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY());
				setBuildingOrTowerDirection(TemporaryModels[2], 0);
				models.addFigure(TemporaryModels[2]);
				TemporaryModels[3] = new Figure(context);
				TemporaryModels[3].setName("ArrowRight");
				TemporaryModels[3].setFigureType(FigureType.general);
				TemporaryModels[3].setPosition(TemporaryModels[5].getX() + 1, TemporaryModels[5].getY() + 1);
				setBuildingOrTowerDirection(TemporaryModels[3], 0);
				models.addFigure(TemporaryModels[3]);
				break;

			}
			break;
			// Chooses between the three different enemy bases
		case BUILDING_CREATION_FINAL_DESTENATION_SELECTION:
			int selectedArrow = 0;
			modelCreationState = MODEL_CREATION_NO_REQUEST_STATE;
			for (int i = 1; i < 4; i++) {
				if (!(TemporaryModels[i].getX() == xTouchPosition && TemporaryModels[i].getY() == yTouchPosition)) {
					models.removeModel(TemporaryModels[i]);
				} else {
					selectedArrow = i;
				}
			}
			switch (selectedArrow) {
			case 0:
				models.removeModel(TemporaryModels[5]);
				break;

			case 1:
				((Building)TemporaryModels[0]).setFinalDestination(players[1].getCentralBase());
				addNewBuilding(0, (Building)TemporaryModels[0]);
				models.removeModel(TemporaryModels[1]);
				models.removeModel(TemporaryModels[5]);
				break;

			case 2:
				((Building)TemporaryModels[0]).setFinalDestination(players[2].getCentralBase());
				addNewBuilding(0, (Building)TemporaryModels[0]);
				models.removeModel(TemporaryModels[2]);
				models.removeModel(TemporaryModels[5]);
				break;

			case 3:
				((Building)TemporaryModels[0]).setFinalDestination(players[3].getCentralBase());
				addNewBuilding(0, (Building)TemporaryModels[0]);
				models.removeModel(TemporaryModels[3]);
				models.removeModel(TemporaryModels[5]);
				break;
			}
		}
	}
	
	// gets the player list
	public Player[] getPlayers() {
		return players;
	}
	// sets the player list
	public void setPlayers(Player[] players) {
		this.players = players;
	}
	
	// exits the game
	public void onGameExit() {
		((Activity)context).finish();
	}

	// sets a display massage
	public void setMessageToDisplay(String messageToDisplay) {
		this.messageToDisplay = messageToDisplay;
	}

	// gets the display massage
	public String getMessageToDisplay() {
		return messageToDisplay;
	}

	// A scheduler for displaying text, moving the camera and enemy behavior.
	public void runSchedule () {
		timeTillNextScheduleState -= LOOP_UPDATE_RATE;
		if (timeTillNextScheduleState <= 0) {
			isEnteringScheduler = true;
			if (isStateEven) {
				currentScheduleState++;
				isStateEven = false;
			} else {
				isStateEven = true;
			}
			timeTillNextScheduleState = timeBetweenSteps;
		}
		switch (currentScheduleState) {
		case INTRODUCTION_1:
			if (isEnteringScheduler == true) {
				moveCanvas (16,5);
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_1;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_2:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_2;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_3:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_3;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_4:
			if (isEnteringScheduler == true) {
				moveCanvas(30, 5);
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_4;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_5:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_5;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_6:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_6;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_7:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_7;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_8:
			if (isEnteringScheduler == true) {
				moveCanvas(0, 5);
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_8;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_9:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_9;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_10:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_10;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_11:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_11;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_12:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_12;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_13:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_13;
				isEnteringScheduler = false;
			}
			break;
		case INTRODUCTION_14:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_14;
				isEnteringScheduler = false;
			}
			break;
		case ENEMY_ADDS_TOWER1:
			if (isEnteringScheduler == true) {
				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_15;
				ArrowTower arrowTower = new ArrowTower(context);
				arrowTower.setPosition(30,5);
				addNewTower(2, arrowTower);
				ArrowTower arrowTower2 = new ArrowTower(context);
				arrowTower2.setPosition(30,10);
				addNewTower(2, arrowTower2);
				isEnteringScheduler = false;
				isStateEven = true;
			}
			break;		
//		case ENEMY_ADDS_BUILDING2:
//			if (isEnteringScheduler == true) {
//				messageToDisplay = IN_GAME_MESSAGE + MESSAGE_NUMBER_16;
//				moveCanvas(30, 5);
//				PhoneixNest phoneixNest = new PhoneixNest(context);
//				phoneixNest.setPosition(32,5);
//				phoneixNest.setFinalDestination(players[0].getCentralBase());
//		        addNewBuilding(2, phoneixNest);		
//				GoblinTavern goblinTavern = new GoblinTavern(context);
//				goblinTavern.setPosition(32,10);
//				goblinTavern.setFinalDestination(players[0].getCentralBase());
//		        addNewBuilding(2, goblinTavern);
//			}
//			break;
		}
	}
	// Moves the camera the desired  destination.
	private void moveCanvas(float xCanvasMoveDestination, float yCanvasMoveDestination) {
		this.xCanvasMoveDestination = xCanvasMoveDestination;
		this.yCanvasMoveDestination = yCanvasMoveDestination;
		this.isCanvasMoving = true;
	}

	// sets true or false in isCanvasMoving
	public void setCanvasMoving(boolean isCanvasMoving) {
		this.isCanvasMoving = isCanvasMoving;
	}

	// getter for isCanvaseMoving
	public boolean isCanvasMoving() {
		return isCanvasMoving;
	}

	// sets the destination for the camera on the canvas in the x axies.
	public void setxCanvasMoveDestination(float xCanvasMoveDestination) {
		this.xCanvasMoveDestination = xCanvasMoveDestination;
	}

	// gets the destination for the camera on the canvas in the x axies
	public float getxCanvasMoveDestination() {
		return xCanvasMoveDestination;
	}

	// sets the destination for the camera on the canvas in the y axies
	public void setyCanvasMoveDestination(float yCanvasMoveDestination) {
		this.yCanvasMoveDestination = yCanvasMoveDestination;
	}

	// gets the destination for the camera on the canvas in the y axies
	public float getyCanvasMoveDestination() {
		return yCanvasMoveDestination;
	}
}
